// ============================================
// cmd/vmctl/commands/vm.go
// ============================================

package commands

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"virt-manager-go/internal/client"
	"virt-manager-go/internal/domain"
)

// 定义获取 serverURL 的函数类型
type ServerURLFunc func() string

// 全局变量存储获取 serverURL 的函数
var getServerURLFunc ServerURLFunc

// 设置获取 serverURL 的函数
func SetServerURLFunc(fn ServerURLFunc) {
	getServerURLFunc = fn
}

// 获取当前运行时的 apiClient 实例
func getAPIClient(logger *logrus.Logger) *client.APIClient {
	// 使用设置的函数获取 serverURL，如果未设置则使用默认值
	serverURL := "http://localhost:8080"
	if getServerURLFunc != nil {
		serverURL = getServerURLFunc()
	}
	return client.NewAPIClient(serverURL, logger)
}

func NewVMCommand(logger *logrus.Logger) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "vm",
		Short: "Manage virtual machines",
		Long:  "Create, start, stop, and manage virtual machines",
	}

	// 使用 RunE 来延迟获取正确的 apiClient 实例
	cmd.AddCommand(
		newVMListCommand(logger),
		newVMShowCommand(logger),
		newVMCreateCommand(logger),
		newVMStartCommand(logger),
		newVMStopCommand(logger),
		newVMRebootCommand(logger),
		newVMDeleteCommand(logger),
		newVMCloneCommand(logger),
		newVMConsoleCommand(logger),
	)

	return cmd
}

// vmctl vm list
func newVMListCommand(logger *logrus.Logger) *cobra.Command {
	var connID string

	cmd := &cobra.Command{
		Use:   "list",
		Short: "List all virtual machines",
		Example: `  vmctl vm list
  vmctl vm list --connection conn-id-123`,
		RunE: func(cmd *cobra.Command, args []string) error {
			// 获取当前运行时的 apiClient 实例
			apiClient := getAPIClient(logger)
			if connID == "" {
				connID = getDefaultConnection(apiClient)
			}

			ctx := context.Background()
			resp, err := apiClient.Get(ctx, fmt.Sprintf("/api/v1/connections/%s/vms", connID))
			if err != nil {
				return err
			}

			var result struct {
				Data []domain.VMInfo `json:"data"`
			}
			if err := json.Unmarshal(resp, &result); err != nil {
				return err
			}

			// 获取输出格式并格式化输出
			outputFormat := GetOutputFormat(cmd.Flag("output").Value.String())
			formatter := NewFormatter(outputFormat)
			return formatter.FormatVMList(result.Data)
		},
	}

	cmd.Flags().StringVarP(&connID, "connection", "c", "", "Connection ID")
	return cmd
}

// vmctl vm show <name>
func newVMShowCommand(logger *logrus.Logger) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "show <name>",
		Short: "Show virtual machine details",
		Args:  cobra.ExactArgs(1),
		Example: `  vmctl vm show ubuntu-vm
  vmctl vm show ubuntu-vm --connection conn-id-123`,
		RunE: func(cmd *cobra.Command, args []string) error {
			vmName := args[0]
			connID := cmd.Flag("connection").Value.String()

			// 获取当前运行时的 apiClient 实例
			apiClient := getAPIClient(logger)
			if connID == "" {
				connID = getDefaultConnection(apiClient)
			}

			ctx := context.Background()
			resp, err := apiClient.Get(ctx, fmt.Sprintf("/api/v1/connections/%s/vms/%s", connID, vmName))
			if err != nil {
				return err
			}

			var result struct {
				Data domain.VMInfo `json:"data"`
			}
			if err := json.Unmarshal(resp, &result); err != nil {
				return err
			}

			// 获取输出格式并格式化输出
			outputFormat := GetOutputFormat(cmd.Flag("output").Value.String())
			formatter := NewFormatter(outputFormat)
			return formatter.FormatVMInfo(&result.Data)
		},
	}

	cmd.Flags().StringP("connection", "c", "", "Connection ID")
	return cmd
}

// vmctl vm create
func newVMCreateCommand(logger *logrus.Logger) *cobra.Command {
	var (
		connID      string
		name        string
		memory      uint64
		cpus        uint
		installISO  string
		installPXE  bool
		importDisk  string
		diskSize    uint64
		networkName string
		graphics    string
		autostart   bool
	)

	cmd := &cobra.Command{
		Use:   "create",
		Short: "Create a new virtual machine",
		Example: `  vmctl vm create --name ubuntu-vm --memory 4096 --cpus 2 --iso /path/to/ubuntu.iso --disk-size 50
  vmctl vm create --name pxe-vm --memory 2048 --cpus 2 --pxe --disk-size 30
  vmctl vm create --name imported-vm --memory 2048 --cpus 2 --import /path/to/disk.qcow2`,
		RunE: func(cmd *cobra.Command, args []string) error {
			apiClient := getAPIClient(logger)
			if connID == "" {
				connID = getDefaultConnection(apiClient)
			}

			if name == "" {
				return fmt.Errorf("VM name is required")
			}

			config := domain.VMCreateConfig{
				Name:      name,
				Memory:    memory,
				CPUs:      cpus,
				Autostart: autostart,
			}

			// 设置安装方式
			if installISO != "" {
				config.InstallISO = installISO
			} else if installPXE {
				config.InstallPXE = true
			} else if importDisk != "" {
				config.ImportDisk = importDisk
			}

			// 添加磁盘
			if diskSize > 0 {
				config.Disks = []*domain.DiskConfig{
					{
						Type:   "file",
						Device: "disk",
						Size:   diskSize,
						Format: "qcow2",
						Bus:    "virtio",
					},
				}
			}

			// 添加网络
			if networkName == "" {
				networkName = "default"
			}
			config.Networks = []*domain.NetworkInterfaceConfig{
				{
					Type:   "network",
					Source: networkName,
					Model:  "virtio",
				},
			}

			// 添加图形设备
			if graphics != "" {
				config.Graphics = &domain.GraphicsConfig{
					Type:   graphics,
					Listen: "0.0.0.0",
					Port:   -1,
				}
			}

			ctx := context.Background()
			resp, err := apiClient.Post(ctx, fmt.Sprintf("/api/v1/connections/%s/vms", connID), config)
			if err != nil {
				return err
			}

			var result struct {
				Data domain.VMInfo `json:"data"`
			}
			if err := json.Unmarshal(resp, &result); err != nil {
				return err
			}

			fmt.Printf("✓ VM '%s' created successfully\n", result.Data.Name)
			fmt.Printf("  UUID: %s\n", result.Data.UUID)
			fmt.Printf("  State: %s\n", result.Data.State)
			return nil
		},
	}

	cmd.Flags().StringVarP(&connID, "connection", "c", "", "Connection ID")
	cmd.Flags().StringVarP(&name, "name", "n", "", "VM name (required)")
	cmd.Flags().Uint64VarP(&memory, "memory", "m", 2048, "Memory in MB")
	cmd.Flags().UintVar(&cpus, "cpus", 2, "Number of CPUs")
	cmd.Flags().StringVar(&installISO, "iso", "", "ISO file path for installation")
	cmd.Flags().BoolVar(&installPXE, "pxe", false, "Use PXE boot")
	cmd.Flags().StringVar(&importDisk, "import", "", "Import existing disk")
	cmd.Flags().Uint64Var(&diskSize, "disk-size", 0, "Disk size in GB")
	cmd.Flags().StringVar(&networkName, "network", "default", "Network name")
	cmd.Flags().StringVar(&graphics, "graphics", "vnc", "Graphics type: vnc, spice")
	cmd.Flags().BoolVar(&autostart, "autostart", false, "Autostart on boot")

	cmd.MarkFlagRequired("name")

	return cmd
}

// vmctl vm start <name>
func newVMStartCommand(logger *logrus.Logger) *cobra.Command {
	var connID string

	cmd := &cobra.Command{
		Use:     "start <vm-name>",
		Short:   "Start a virtual machine",
		Args:    cobra.ExactArgs(1),
		Example: `  vmctl vm start ubuntu-vm`,
		RunE: func(cmd *cobra.Command, args []string) error {
			apiClient := getAPIClient(logger)
			if connID == "" {
				connID = getDefaultConnection(apiClient)
			}

			vmName := args[0]
			ctx := context.Background()
			_, err := apiClient.Post(ctx, fmt.Sprintf("/api/v1/connections/%s/vms/%s/start", connID, vmName), nil)
			if err != nil {
				return err
			}

			fmt.Printf("✓ VM '%s' started successfully\n", vmName)
			return nil
		},
	}

	cmd.Flags().StringVarP(&connID, "connection", "c", "", "Connection ID")
	return cmd
}

// vmctl vm stop <name>
func newVMStopCommand(logger *logrus.Logger) *cobra.Command {
	var (
		connID string
		force  bool
	)

	cmd := &cobra.Command{
		Use:   "stop <vm-name>",
		Short: "Stop a virtual machine",
		Args:  cobra.ExactArgs(1),
		Example: `  vmctl vm stop ubuntu-vm
  vmctl vm stop ubuntu-vm --force`,
		RunE: func(cmd *cobra.Command, args []string) error {
			apiClient := getAPIClient(logger)
			if connID == "" {
				connID = getDefaultConnection(apiClient)
			}

			vmName := args[0]
			path := fmt.Sprintf("/api/v1/connections/%s/vms/%s/shutdown", connID, vmName)
			if force {
				path += "?force=true"
			}

			ctx := context.Background()
			_, err := apiClient.Post(ctx, path, nil)
			if err != nil {
				return err
			}

			if force {
				fmt.Printf("✓ VM '%s' force stopped\n", vmName)
			} else {
				fmt.Printf("✓ VM '%s' shutdown initiated\n", vmName)
			}
			return nil
		},
	}

	cmd.Flags().StringVarP(&connID, "connection", "c", "", "Connection ID")
	cmd.Flags().BoolVarP(&force, "force", "f", false, "Force stop")
	return cmd
}

// vmctl vm reboot <name>
func newVMRebootCommand(logger *logrus.Logger) *cobra.Command {
	var connID string

	cmd := &cobra.Command{
		Use:   "reboot <vm-name>",
		Short: "Reboot a virtual machine",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			apiClient := getAPIClient(logger)
			if connID == "" {
				connID = getDefaultConnection(apiClient)
			}

			vmName := args[0]
			ctx := context.Background()
			_, err := apiClient.Post(ctx, fmt.Sprintf("/api/v1/connections/%s/vms/%s/reboot", connID, vmName), nil)
			if err != nil {
				return err
			}

			fmt.Printf("✓ VM '%s' rebooted\n", vmName)
			return nil
		},
	}

	cmd.Flags().StringVarP(&connID, "connection", "c", "", "Connection ID")
	return cmd
}

// vmctl vm delete <name>
func newVMDeleteCommand(logger *logrus.Logger) *cobra.Command {
	var (
		connID        string
		removeStorage bool
		confirm       bool
	)

	cmd := &cobra.Command{
		Use:   "delete <vm-name>",
		Short: "Delete a virtual machine",
		Args:  cobra.ExactArgs(1),
		Example: `  vmctl vm delete old-vm --yes
  vmctl vm delete old-vm --remove-storage --yes`,
		RunE: func(cmd *cobra.Command, args []string) error {
			apiClient := getAPIClient(logger)
			if connID == "" {
				connID = getDefaultConnection(apiClient)
			}

			vmName := args[0]

			if !confirm {
				fmt.Printf("Are you sure you want to delete VM '%s'? [y/N]: ", vmName)
				var response string
				fmt.Scanln(&response)
				if strings.ToLower(response) != "y" {
					fmt.Println("Cancelled")
					return nil
				}
			}

			path := fmt.Sprintf("/api/v1/connections/%s/vms/%s", connID, vmName)
			if removeStorage {
				path += "?remove_storage=true"
			}

			ctx := context.Background()
			_, err := apiClient.Delete(ctx, path)
			if err != nil {
				return err
			}

			fmt.Printf("✓ VM '%s' deleted\n", vmName)
			return nil
		},
	}

	cmd.Flags().StringVarP(&connID, "connection", "c", "", "Connection ID")
	cmd.Flags().BoolVar(&removeStorage, "remove-storage", false, "Remove storage volumes")
	cmd.Flags().BoolVarP(&confirm, "yes", "y", false, "Skip confirmation")
	return cmd
}

// vmctl vm clone <source> <dest>
func newVMCloneCommand(logger *logrus.Logger) *cobra.Command {
	var connID string

	cmd := &cobra.Command{
		Use:     "clone <source-vm> <dest-vm>",
		Short:   "Clone a virtual machine",
		Args:    cobra.ExactArgs(2),
		Example: `  vmctl vm clone ubuntu-vm ubuntu-vm-clone`,
		RunE: func(cmd *cobra.Command, args []string) error {
			apiClient := getAPIClient(logger)
			if connID == "" {
				connID = getDefaultConnection(apiClient)
			}

			srcVM := args[0]
			destVM := args[1]

			ctx := context.Background()
			_, err := apiClient.Post(ctx,
				fmt.Sprintf("/api/v1/connections/%s/vms/%s/clone", connID, srcVM),
				map[string]string{"name": destVM})
			if err != nil {
				return err
			}

			fmt.Printf("✓ VM cloned: %s → %s\n", srcVM, destVM)
			return nil
		},
	}

	cmd.Flags().StringVarP(&connID, "connection", "c", "", "Connection ID")
	return cmd
}

// vmctl vm console <name>
func newVMConsoleCommand(logger *logrus.Logger) *cobra.Command {
	var connID string

	cmd := &cobra.Command{
		Use:     "console <vm-name>",
		Short:   "Connect to VM serial console",
		Args:    cobra.ExactArgs(1),
		Example: `  vmctl vm console ubuntu-vm`,
		RunE: func(cmd *cobra.Command, args []string) error {
			vmName := args[0]
			fmt.Printf("Opening console to '%s'...\n", vmName)
			fmt.Println("(This would open WebSocket connection to serial console)")
			fmt.Println("Press Ctrl+] to exit")

			// TODO: 实现 WebSocket 连接到串行控制台
			return fmt.Errorf("console connection not yet implemented in CLI")
		},
	}

	cmd.Flags().StringVarP(&connID, "connection", "c", "", "Connection ID")
	return cmd
}

// === 辅助函数 ===

func getDefaultConnection(apiClient *client.APIClient) string {
	ctx := context.Background()
	resp, err := apiClient.Get(ctx, "/api/v1/connections")
	if err != nil {
		return ""
	}

	var result struct {
		Data []domain.ConnectionInfo `json:"data"`
	}
	if err := json.Unmarshal(resp, &result); err != nil || len(result.Data) == 0 {
		return ""
	}

	return result.Data[0].ID
}
